Skip to content

[runtimes][PAC] Harden unwinding when possible (#138571) #143230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ojhunt
Copy link
Contributor

@ojhunt ojhunt commented Jun 7, 2025

This hardens the unwinding logic and datastructures on systems that support pointer authentication.

The approach taken to hardening is to harden the schemas of as many high value fields in the myriad structs as possible, and then also explicitly qualify local variables referencing privileged or security critical values.

This ABI is exposed to the personality functions, and so updating to conform to that is a mandatory change, but to reduce the risk of oracles, the adoption also hardened the locals and datastructures in compiler-rt and libcxxabi.

We're gating these on defined(__APPLE__) so other platforms are able to qualify before enabling. An alternative would be a feature flag that could be used instead but I didn't want to force such a change if it was not considered
necessary.

@ojhunt ojhunt requested review from asl, ahmedbougacha and ldionne June 7, 2025 02:10
@ojhunt ojhunt requested review from a team as code owners June 7, 2025 02:10
@ojhunt ojhunt added compiler-rt libc++abi libc++abi C++ Runtime Library. Not libc++. libunwind hardening Issues related to the hardening effort labels Jun 7, 2025
@llvmbot llvmbot added compiler-rt:builtins PGO Profile Guided Optimizations labels Jun 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Jun 7, 2025

@llvm/pr-subscribers-pgo
@llvm/pr-subscribers-libcxxabi

@llvm/pr-subscribers-libunwind

Author: Oliver Hunt (ojhunt)

Changes

This hardens the unwinding logic and datastructures on systems that support pointer authentication.

The approach taken to hardening is to harden the schemas of as many high value fields in the myriad structs as possible, and then also explicitly qualify local variables referencing privileged or security critical values.

This ABI is exposed to the personality functions, and so updating to conform to that is a mandatory change, but to reduce the risk of oracles, the adoption also hardened the locals and datastructures in compiler-rt and libcxxabi.

We're gating these on defined(__APPLE__) so other platforms are able to qualify before enabling. An alternative would be an feature flag that could be used instead but I didn't want to force such a change if it was not considered
necessary.


Patch is 51.36 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/143230.diff

14 Files Affected:

  • (modified) compiler-rt/lib/builtins/gcc_personality_v0.c (+63-6)
  • (modified) compiler-rt/lib/profile/InstrProfilingValue.c (+7-1)
  • (modified) libcxxabi/include/__cxxabi_config.h (+47-1)
  • (modified) libcxxabi/src/cxa_exception.h (+16-14)
  • (modified) libcxxabi/src/cxa_personality.cpp (+61-8)
  • (modified) libunwind/include/libunwind.h (+70-8)
  • (modified) libunwind/src/AddressSpace.hpp (+30-14)
  • (modified) libunwind/src/DwarfInstructions.hpp (+8-3)
  • (modified) libunwind/src/DwarfParser.hpp (+40-4)
  • (modified) libunwind/src/Registers.hpp (+109-4)
  • (modified) libunwind/src/UnwindCursor.hpp (+55-17)
  • (modified) libunwind/src/UnwindLevel1.c (+30-6)
  • (modified) libunwind/src/UnwindRegistersSave.S (+10)
  • (modified) libunwind/src/libunwind.cpp (+34)
diff --git a/compiler-rt/lib/builtins/gcc_personality_v0.c b/compiler-rt/lib/builtins/gcc_personality_v0.c
index ef63a5fb83472..42b992949d2cc 100644
--- a/compiler-rt/lib/builtins/gcc_personality_v0.c
+++ b/compiler-rt/lib/builtins/gcc_personality_v0.c
@@ -30,6 +30,45 @@ EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,
                                             _Unwind_Personality_Fn);
 #endif
 
+#if __has_include(<ptrauth.h>)
+#include <ptrauth.h>
+#endif
+
+#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
+#if __has_feature(ptrauth_restricted_intptr_qualifier)
+#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated,       \
+                                              discriminatorString)             \
+    __ptrauth_restricted_intptr(key, addressDiscriminated,                     \
+                            ptrauth_string_discriminator(discriminatorString))
+#else
+#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated,       \
+                                              discriminatorString)             \
+    __ptrauth(key, addressDiscriminated,                                       \
+              ptrauth_string_discriminator(discriminatorString))
+#endif
+#else
+#define PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(key, addressDiscriminated,       \
+                                              discriminatorString)
+#endif
+
+// Helper wrappers for pointer auth qualifiers because we use a lot of variants
+// Suffixes:
+//  * PDC : ptrauth_key_process_dependent_code
+//  * RA  : ptrauth_key_return_address
+//  * FN  : ptrauth_key_function_pointer
+#define PERSONALITY_PTRAUTH_RI_FN(__discriminator)                             \
+    PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_function_pointer,        \
+                       /*__address_discriminated=*/1,                          \
+                       __discriminator)
+#define PERSONALITY_PTRAUTH_RI_PDC(__discriminator)                            \
+    PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_code,  \
+                       /*__address_discriminated=*/1,                          \
+                       __discriminator)
+#define PERSONALITY_PTRAUTH_RI_RA(__discriminator)                             \
+    PERSONALITY_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_return_address,          \
+                       /*__address_discriminated=*/1,                          \
+                       __discriminator)
+
 // Pointer encodings documented at:
 //   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
 
@@ -205,7 +244,8 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
     return continueUnwind(exceptionObject, context);
 
   uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
-  uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
+  uintptr_t PERSONALITY_PTRAUTH_RI_FN("__gcc_personality_v0'funcStart")
+      funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
   uintptr_t pcOffset = pc - funcStart;
 
   // Parse LSDA header.
@@ -224,11 +264,14 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
   const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
   const uint8_t *p = callSiteTableStart;
   while (p < callSiteTableEnd) {
-    uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
-    size_t length = readEncodedPointer(&p, callSiteEncoding);
-    size_t landingPad = readEncodedPointer(&p, callSiteEncoding);
+    uintptr_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'start")
+        start = readEncodedPointer(&p, callSiteEncoding);
+    size_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'length")
+        length = readEncodedPointer(&p, callSiteEncoding);
+    size_t PERSONALITY_PTRAUTH_RI_PDC("__gcc_personality_v0'landingPadOffset")
+        landingPadOffset = readEncodedPointer(&p, callSiteEncoding);
     readULEB128(&p); // action value not used for C code
-    if (landingPad == 0)
+    if (landingPadOffset == 0)
       continue; // no landing pad for this entry
     if ((start <= pcOffset) && (pcOffset < (start + length))) {
       // Found landing pad for the PC.
@@ -238,7 +281,21 @@ COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
       _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
                     (uintptr_t)exceptionObject);
       _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
-      _Unwind_SetIP(context, (funcStart + landingPad));
+#define LANDING_PAD_DISCRIMINATOR "__gcc_personality_v0'landingPad"
+      size_t PERSONALITY_PTRAUTH_RI_RA(LANDING_PAD_DISCRIMINATOR)
+          landingPad = funcStart + landingPadOffset;
+#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
+      uintptr_t stack_pointer = _Unwind_GetGR(context, -2);
+      const uintptr_t existingDiscriminator = ptrauth_blend_discriminator(
+          &landingPad,
+          ptrauth_string_discriminator(LANDING_PAD_DISCRIMINATOR));
+      uintptr_t newIP = (uintptr_t)ptrauth_auth_and_resign(
+          *(void **)&landingPad, ptrauth_key_function_pointer,
+          existingDiscriminator, ptrauth_key_return_address, stack_pointer);
+      _Unwind_SetIP(context, newIP);
+#else
+      _Unwind_SetIP(context, landingPad);
+#endif
       return _URC_INSTALL_CONTEXT;
     }
   }
diff --git a/compiler-rt/lib/profile/InstrProfilingValue.c b/compiler-rt/lib/profile/InstrProfilingValue.c
index a608d41d39e77..cd6ae3d7a4248 100644
--- a/compiler-rt/lib/profile/InstrProfilingValue.c
+++ b/compiler-rt/lib/profile/InstrProfilingValue.c
@@ -83,7 +83,13 @@ __llvm_profile_iterate_data(const __llvm_profile_data *Data) {
 /* This method is only used in value profiler mock testing.  */
 COMPILER_RT_VISIBILITY void *
 __llvm_get_function_addr(const __llvm_profile_data *Data) {
-  return Data->FunctionPointer;
+  void *FP = Data->FunctionPointer;
+#if __has_feature(ptrauth_calls)
+  // This is only used for tests where we compare against what happens to be
+  // signed pointers.
+  FP = ptrauth_sign_unauthenticated(FP, VALID_CODE_KEY, 0);
+#endif
+   return FP;
 }
 
 /* Allocate an array that holds the pointers to the linked lists of
diff --git a/libcxxabi/include/__cxxabi_config.h b/libcxxabi/include/__cxxabi_config.h
index 759445dac91f9..e67d065fe57f3 100644
--- a/libcxxabi/include/__cxxabi_config.h
+++ b/libcxxabi/include/__cxxabi_config.h
@@ -32,7 +32,8 @@
 #endif
 
 #if defined(_WIN32)
- #if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) || (defined(__MINGW32__) && !defined(_LIBCXXABI_BUILDING_LIBRARY))
+ #if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) ||                     \
+     (defined(__MINGW32__) && !defined(_LIBCXXABI_BUILDING_LIBRARY))
   #define _LIBCXXABI_HIDDEN
   #define _LIBCXXABI_DATA_VIS
   #define _LIBCXXABI_FUNC_VIS
@@ -109,4 +110,49 @@
 #  define _LIBCXXABI_NOEXCEPT noexcept
 #endif
 
+#if __has_include(<ptrauth.h>)
+#include <ptrauth.h>
+#endif
+
+#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
+#  define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator)  \
+    __ptrauth(__key,__address_discriminated,                                   \
+              ptrauth_string_discriminator(__discriminator))
+// This work around is required to support divergence in spelling
+// during the ptrauth upstreaming process.
+#  if __has_feature(ptrauth_restricted_intptr_qualifier)
+#  define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \
+                                               __discriminator)                \
+    __ptrauth_restricted_intptr(__key,__address_discriminated,                 \
+                                ptrauth_string_discriminator(__discriminator))
+#  else
+#  define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \
+                                               __discriminator)                \
+    __ptrauth(__key,__address_discriminated,                                   \
+              ptrauth_string_discriminator(__discriminator))
+#  endif
+#else
+#  define _LIBCXXABI_PTRAUTH(__key, __address_discriminated, __discriminator)
+#  define _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated, \
+                                               __discriminator)
+#endif
+
+// Helper wrappers for pointer auth qualifiers because we use a lot of variants
+// Suffixes:
+//  * _RI : qualifier is __ptrauth_restricted_intptr
+//  * PDD : key is ptrauth_key_process_dependent_data
+//  * FN  : key is ptrauth_key_function_pointer
+#define _LIBCXXABI_PTRAUTH_PDD(__discriminator)                                \
+    _LIBCXXABI_PTRAUTH(ptrauth_key_process_dependent_data,                     \
+                       /*__address_discriminated=*/1,                          \
+                       __discriminator)
+#define _LIBCXXABI_PTRAUTH_FN(__discriminator)                                 \
+    _LIBCXXABI_PTRAUTH(ptrauth_key_function_pointer,                           \
+                       /*__address_discriminated=*/1,                          \
+                       __discriminator)
+#define _LIBCXXABI_PTRAUTH_RI_PDD(__discriminator)                             \
+    _LIBCXXABI_PTRAUTH_RESTRICTED_INTPTR(ptrauth_key_process_dependent_data,   \
+                                         /*__address_discriminated=*/1,        \
+                                         __discriminator)
+
 #endif // ____CXXABI_CONFIG_H
diff --git a/libcxxabi/src/cxa_exception.h b/libcxxabi/src/cxa_exception.h
index aba08f2992103..4c69d48048f02 100644
--- a/libcxxabi/src/cxa_exception.h
+++ b/libcxxabi/src/cxa_exception.h
@@ -47,10 +47,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
     // In Wasm, a destructor returns its argument
     void *(_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
 #else
-    void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
+    void(_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH_FN("__cxa_exception::exceptionDestructor") exceptionDestructor)(void*);
 #endif
-    std::unexpected_handler unexpectedHandler;
-    std::terminate_handler  terminateHandler;
+    std::unexpected_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::unexpectedHandler") unexpectedHandler;
+    std::terminate_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::terminateHandler") terminateHandler;
 
     __cxa_exception *nextException;
 
@@ -61,10 +61,10 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
     int propagationCount;
 #else
     int handlerSwitchValue;
-    const unsigned char *actionRecord;
-    const unsigned char *languageSpecificData;
-    void *catchTemp;
-    void *adjustedPtr;
+    const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::actionRecord") actionRecord;
+    const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::languageSpecificData") languageSpecificData;
+    void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::catchTemp") catchTemp;
+    void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::adjustedPtr") adjustedPtr;
 #endif
 
 #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)
@@ -79,6 +79,8 @@ struct _LIBCXXABI_HIDDEN __cxa_exception {
 // http://sourcery.mentor.com/archives/cxx-abi-dev/msg01924.html
 // The layout of this structure MUST match the layout of __cxa_exception, with
 // primaryException instead of referenceCount.
+// The tags used in the pointer authentication qualifiers also need to match
+// those of the corresponding members in __cxa_exception.
 struct _LIBCXXABI_HIDDEN __cxa_dependent_exception {
 #if defined(__LP64__) || defined(_WIN64) || defined(_LIBCXXABI_ARM_EHABI)
     void* reserve; // padding.
@@ -86,9 +88,9 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception {
 #endif
 
     std::type_info *exceptionType;
-    void (_LIBCXXABI_DTOR_FUNC *exceptionDestructor)(void *);
-    std::unexpected_handler unexpectedHandler;
-    std::terminate_handler terminateHandler;
+    void(_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH_FN("__cxa_exception::exceptionDestructor") exceptionDestructor)(void*);
+    std::unexpected_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::unexpectedHandler") unexpectedHandler;
+    std::terminate_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::terminateHandler") terminateHandler;
 
     __cxa_exception *nextException;
 
@@ -99,10 +101,10 @@ struct _LIBCXXABI_HIDDEN __cxa_dependent_exception {
     int propagationCount;
 #else
     int handlerSwitchValue;
-    const unsigned char *actionRecord;
-    const unsigned char *languageSpecificData;
-    void * catchTemp;
-    void *adjustedPtr;
+    const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::actionRecord") actionRecord;
+    const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::languageSpecificData") languageSpecificData;
+    void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::catchTemp") catchTemp;
+    void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::adjustedPtr") adjustedPtr;
 #endif
 
 #if !defined(__LP64__) && !defined(_WIN64) && !defined(_LIBCXXABI_ARM_EHABI)
diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp
index 5f6e75c5be19c..cbb3f46e0f55c 100644
--- a/libcxxabi/src/cxa_personality.cpp
+++ b/libcxxabi/src/cxa_personality.cpp
@@ -22,6 +22,12 @@
 #include "private_typeinfo.h"
 #include "unwind.h"
 
+#if __has_include(<ptrauth.h>)
+#include <ptrauth.h>
+#endif
+
+#include "libunwind.h"
+
 // TODO: This is a temporary workaround for libc++abi to recognize that it's being
 // built against LLVM's libunwind. LLVM's libunwind started reporting _LIBUNWIND_VERSION
 // in LLVM 15 -- we can remove this workaround after shipping LLVM 17. Once we remove
@@ -527,12 +533,19 @@ get_thrown_object_ptr(_Unwind_Exception* unwind_exception)
 namespace
 {
 
+#define _LIBCXXABI_PTRAUTH_KEY ptrauth_key_process_dependent_code
+typedef const uint8_t* _LIBCXXABI_PTRAUTH_PDD("scan_results::languageSpecificData") lsd_ptr_t;
+typedef const uint8_t* _LIBCXXABI_PTRAUTH_PDD("scan_results::actionRecord") action_ptr_t;
+#define _LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC "scan_results::landingPad"
+typedef uintptr_t _LIBCXXABI_PTRAUTH_RI_PDD(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC) landing_pad_t;
+typedef void* _LIBCXXABI_PTRAUTH_PDD(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC) landing_pad_ptr_t;
+
 struct scan_results
 {
     int64_t        ttypeIndex;   // > 0 catch handler, < 0 exception spec handler, == 0 a cleanup
-    const uint8_t* actionRecord;         // Currently unused.  Retained to ease future maintenance.
-    const uint8_t* languageSpecificData;  // Needed only for __cxa_call_unexpected
-    uintptr_t      landingPad;   // null -> nothing found, else something found
+    action_ptr_t actionRecord;   // Currently unused.  Retained to ease future maintenance.
+    lsd_ptr_t languageSpecificData; // Needed only for __cxa_call_unexpected
+    landing_pad_t landingPad;       // null -> nothing found, else something found
     void*          adjustedPtr;  // Used in cxa_exception.cpp
     _Unwind_Reason_Code reason;  // One of _URC_FATAL_PHASE1_ERROR,
                                  //        _URC_FATAL_PHASE2_ERROR,
@@ -541,7 +554,33 @@ struct scan_results
 };
 
 }  // unnamed namespace
+}
 
+namespace {
+// The logical model for casting authenticated function pointers makes
+// it impossible to directly cast them without breaking the authentication,
+// as a result we need this pair of helpers.
+template <typename PtrType>
+void set_landing_pad_as_ptr(scan_results& results, const PtrType& out) {
+  union {
+    landing_pad_t* as_landing_pad;
+    landing_pad_ptr_t* as_pointer;
+  } u;
+  u.as_landing_pad = &results.landingPad;
+  *u.as_pointer = out;
+}
+
+static const landing_pad_ptr_t& get_landing_pad_as_ptr(const scan_results& results) {
+  union {
+    const landing_pad_t* as_landing_pad;
+    const landing_pad_ptr_t* as_pointer;
+  } u;
+  u.as_landing_pad = &results.landingPad;
+  return *u.as_pointer;
+}
+} // unnamed namespace
+
+extern "C" {
 static
 void
 set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context,
@@ -557,7 +596,22 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context,
                 reinterpret_cast<uintptr_t>(unwind_exception));
   _Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
                 static_cast<uintptr_t>(results.ttypeIndex));
+#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
+  auto stack_pointer = _Unwind_GetGR(context, UNW_REG_SP);
+  // We manually re-sign the IP as the __ptrauth qualifiers cannot
+  // express the required relationship with the destination address
+  const auto existingDiscriminator = ptrauth_blend_discriminator(
+      &results.landingPad,
+      ptrauth_string_discriminator(_LIBCXXABI_PTRAUTH_SCANRESULT_LANDINGPAD_DISC));
+  unw_word_t newIP = (unw_word_t)ptrauth_auth_and_resign(*(void**)&results.landingPad,
+                                                         _LIBCXXABI_PTRAUTH_KEY,
+                                                         existingDiscriminator,
+                                                         ptrauth_key_return_address,
+                                                         stack_pointer);
+  _Unwind_SetIP(context, newIP);
+#else
   _Unwind_SetIP(context, results.landingPad);
+#endif
 }
 
 /*
@@ -691,12 +745,12 @@ static void scan_eh_tab(scan_results &results, _Unwind_Action actions,
         // The call sites are ordered in increasing value of start
         uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);
         uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);
-        uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
+        landing_pad_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);
         uintptr_t actionEntry = readULEB128(&callSitePtr);
         if ((start <= ipOffset) && (ipOffset < (start + length)))
 #else  // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__
         // ip is 1-based index into this table
-        uintptr_t landingPad = readULEB128(&callSitePtr);
+        landing_pad_t landingPad = readULEB128(&callSitePtr);
         uintptr_t actionEntry = readULEB128(&callSitePtr);
         if (--ip == 0)
 #endif // __USING_SJLJ_EXCEPTIONS__ || __WASM_EXCEPTIONS__
@@ -935,8 +989,7 @@ __gxx_personality_v0
         results.ttypeIndex = exception_header->handlerSwitchValue;
         results.actionRecord = exception_header->actionRecord;
         results.languageSpecificData = exception_header->languageSpecificData;
-        results.landingPad =
-            reinterpret_cast<uintptr_t>(exception_header->catchTemp);
+        set_landing_pad_as_ptr(results, exception_header->catchTemp);
         results.adjustedPtr = exception_header->adjustedPtr;
 
         // Jump to the handler.
@@ -970,7 +1023,7 @@ __gxx_personality_v0
             exc->handlerSwitchValue = static_cast<int>(results.ttypeIndex);
             exc->actionRecord = results.actionRecord;
             exc->languageSpecificData = results.languageSpecificData;
-            exc->catchTemp = reinterpret_cast<void*>(results.landingPad);
+            exc->catchTemp = get_landing_pad_as_ptr(results);
             exc->adjustedPtr = results.adjustedPtr;
 #ifdef __WASM_EXCEPTIONS__
             // Wasm only uses a single phase (_UA_SEARCH_PHASE), so save the
diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h
index b2dae8feed9a3..e7375bbca1b3d 100644
--- a/libunwind/include/libunwind.h
+++ b/libunwind/include/libunwind.h
@@ -43,6 +43,61 @@
   #define LIBUNWIND_AVAIL
 #endif
 
+#if __has_include(<ptrauth.h>)
+#include <ptrauth.h>
+#endif
+
+#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
+#define _LIBUNWIND_PTRAUTH(__key, __address_discriminated, __discriminator)    \
+  __ptrauth(__key, __address_discriminated,                                    \
+            ptrauth_string_discriminator(__discriminator))
+// This work around is required to support divergence in spelling
+// developed during the ptrauth upstreaming process.
+#if __has_feature(ptrauth_restricted_intptr_qualifier)
+#define _LIBUNWIND_PTRAUTH_RESTRICTED_INTPTR(__key, __address_discriminated,   \
+                                             __discriminator)                  \
+  __ptrauth_restricted_intptr(__key, __address_discriminated,                  \
+             ptrauth_string_discriminator(__discriminator...
[truncated]

Copy link

github-actions bot commented Jun 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@ojhunt ojhunt force-pushed the users/ojhunt/pointer-authenticated-unwinding branch from ad78fee to 6810396 Compare June 7, 2025 02:19
This hardens the unwinding logic and datastructures on systems
that support pointer authentication.

The approach taken to hardening is to harden the schemas of as many
high value fields in the myriad structs as possible, and then also
explicitly qualify local variables referencing privileged or security
critical values.

This ABI is exposed to the personality functions, and so updating to
conform to that is a mandatory change, but to reduce the risk of
oracles, the adoption also hardened the locals and datastructures
in compiler-rt and libcxxabi.
@ojhunt ojhunt force-pushed the users/ojhunt/pointer-authenticated-unwinding branch from 6810396 to 637245f Compare June 7, 2025 02:22
@asl
Copy link
Collaborator

asl commented Jun 7, 2025

@kovdan01 @atrosinenko Please take a look

@ojhunt
Copy link
Contributor Author

ojhunt commented Jun 7, 2025

Updating formatting before review - had discussed with Louis and he expressed a preference for some of it, but this llvm.org style bot complains many other cases (likely a local config issue when I was trying to cleanup the downstream code.

So I've updated with a direct clang-format fix.

@ojhunt
Copy link
Contributor Author

ojhunt commented Jun 8, 2025

unused variable errors are likely fallout from refactoring and workaround removals I did as part of the prep, will do some cleanup/diagnostics work later this week.

I mostly wanted to get this available to others to see whether they were ok adopting this rather than rolling their own version.

size_t PERSONALITY_PTRAUTH_RI_RA(LANDING_PAD_DISCRIMINATOR) landingPad =
funcStart + landingPadOffset;
#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
uintptr_t stack_pointer = _Unwind_GetGR(context, -2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] It seems like stackPointer better aligns with the prevailing naming style of this file.

@@ -557,7 +596,19 @@ set_registers(_Unwind_Exception* unwind_exception, _Unwind_Context* context,
reinterpret_cast<uintptr_t>(unwind_exception));
_Unwind_SetGR(context, __builtin_eh_return_data_regno(1),
static_cast<uintptr_t>(results.ttypeIndex));
#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
auto stack_pointer = _Unwind_GetGR(context, UNW_REG_SP);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Possibly incorrect naming style: stack_pointer (though, names in this file doesn't seem very consistent anyway).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I spent a bunch of time trying to get the spellings more consistent but I really wasn't sure :-/

@@ -1974,6 +1994,17 @@ bool UnwindCursor<A, R>::getInfoFromCompactEncodingSection(pint_t pc,
personalityIndex * sizeof(uint32_t));
pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta;
personality = _addressSpace.getP(personalityPointer);
#if __has_feature(ptrauth_calls)
// The GOT for the personality function was signed address authenticated.
// Resign is as a regular function pointer.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Typo?

Suggested change
// Resign is as a regular function pointer.
// Resign it as a regular function pointer.

Comment on lines +709 to +711
#if __has_feature(ptrauth_calls)
retab
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly ptrauth_returns intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see earlier comment - do we want to just adopt a single flag instead of the current mix of ptrauth_calls and ptrauth_returns ?

Comment on lines +771 to +773
#if __has_feature(ptrauth_calls)
pacibsp
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly ptrauth_returns intended?

Comment on lines +816 to +818
#if __has_feature(ptrauth_calls)
retab
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly ptrauth_returns intended?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what we have downstream, it's possible we've just developed an assumption that they're equivalent at this point.

That said I'm actually thinking this should also be around an if __APPLE__ as well pending adoption of/a more generic gate on the ABI.

assert(stripped_value >= info.start_ip && stripped_value <= info.end_ip);
#endif

pint_t sp = (pint_t)co->getReg(UNW_REG_SP);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pint_t sp = (pint_t)co->getReg(UNW_REG_SP);
pint_t sp = (pint_t)co->getReg(UNW_REG_SP);
(void)sp;

This should quiet the unused variable warnings.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was going to actually look at this to see if even needs to be outside of an ifdef

#define _LIBCXXABI_FUNC_VIS __declspec(dllimport)
#define _LIBCXXABI_TYPE_VIS __declspec(dllimport)
#endif
# if defined(_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS) || \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's avoid changing the formatting of unrelated pieces of code. It's fine if the clang-format job is not happy -- we haven't run clang-format on libc++abi and libunwind yet, unless I mis-remember.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how/why/when I changed this formatting because I'd swear I went through trying to remove them all

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I bet I auto-ran clang-format after the bots complained or reflexively pre-push to avoid the bots complain. Sighhhhh

#endif
}

inline Registers_arm64::Registers_arm64(const Registers_arm64 &other) {
Copy link
Contributor

@kovdan01 kovdan01 Jun 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Declarations of these copy constructor and corresponding copy assignment operator are missing in the class body, so this basically does not seem to compile (at least I was unable to do that :) ).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm, I'll need to do some hunting to work out if this code is dead, or if I missed some declaration from

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just revealed that at least operator= is not dead code. It's called from stepWithDwarf. So yeah, you probably just need to add declarations.

@@ -93,6 +98,13 @@ class _LIBUNWIND_HIDDEN Registers_x86 {
uint32_t getEDI() const { return _registers.__edi; }
void setEDI(uint32_t value) { _registers.__edi = value; }

typedef uint32_t reg_t;
typedef uint32_t link_reg_t;
void loadAndAuthenticateLinkRegister(reg_t srcLinkRegister,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since loadAndAuthenticateLinkRegister is used in UnwindCursor.hpp as a member function of a registers object (having template registers class type), we should probably consider one of the following options.

  1. Define loadAndAuthenticateLinkRegister for all the register classes. As far as I can see now, your patch contains definitions for x86, x86_64, ppc, arm64 and arm, but we also have mips, risc-v, ...
  2. Only limit this function to arm64 and do not expose that to register classes for other architectures. In this case, you'll probably need to insert checks like __has_feature(...) in UnwindCursor.hpp and add special logic in these cases.

I personally prefer the 2nd option. It's not very nice, but IMHO this is better than adding a placeholder function for all the architectures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, especially as it's clearly difficult to ensure with local builds that you've got every other platform correct, and then it's make-work for every other platform.

__LIBUNWIND_PTRAUTH_RI_PDC("Registers_arm64::link_reg_t") link_reg_t;
void
loadAndAuthenticateLinkRegister(reg_t inplaceAuthedLinkRegister,
link_reg_t *referenceAuthedLinkRegister) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: probably it's worth using l-value reference instead of a pointer. We assume this being non-null, and actually each time you call this in UnwindCursor.hpp you are taking an address of a variable. L-value reference seems to be a perfect fit for this situation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I vastly prefer using pointers for out parameters as it makes it much clearer at the call site that it's an out parameter.

Ideally we'd just return the pointer but the problem is we can't have ptrauth-qualified return values and as a result it's a foot gun for an oracle.

Alternatively I could create a struct wrapper to force the authentication, but the problem there is that I think the design would want to construct a dependent ptrauth qualifier and we don't yet support that.

Or I guess we could just make a

template <class T> PointerAuthQualifierPointer = is_pointer<T> && (!ptrauth_available || ptrauth_schema_query_has_pointer_auth(T));
template < PointerAuthQualifierPointer T> struct PtrauthQualifiedPointer {
  T thePointer;
  ...
 }

To use for parameters and return values we consider important enough to require constant protection.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, let's just stick with current approach as for now, thanks for explanation

void
loadAndAuthenticateLinkRegister(reg_t inplaceAuthedLinkRegister,
link_reg_t *referenceAuthedLinkRegister) {
#if __has_feature(ptrauth_calls)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably, you meant ptrauth_returns. I saw your discussion with @atrosinenko regarding similar situations in assembly files and I do get the point that these were assumed identical for your purposes in downstream. But it looks like that it's time to change this to ptrauth_returns. It's worth looking at other occurrences of ptrauth_calls as well - at least some of them are also probably misused and should be replaced with ptrauth_returns.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use a wide array of ptrauth_returns and ptrauth_calls, do we just want a single flag/define we should use?

I think the variation we see here is the outcome of changes over time, but it would seem reasonable to maybe just have a single mode flag at this point?

Copy link
Contributor

@atrosinenko atrosinenko Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I get it right, ptrauth_returns do not influence the ABI and ptrauth_calls do, thus it may be trivial to enable pac-ret in one's own build, but ptrauth_calls have to be consistently enabled for the whole runtime.

UPD: Well, it doesn't influence ABI most of the time, otherwise we would only have to mention it around paciasp/autiasp/retaa in hand-written assembly :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah yeah, you are correct, in principle pac-ret is safely separable so yeah I'll need to go through and audit which should be gated by ptrauth_calls vs _return and which are incorrect due to being able to assume both are enabled.

@@ -1877,6 +1946,32 @@ inline Registers_arm64::Registers_arm64(const void *registers) {
memcpy(_vectorHalfRegisters,
static_cast<const uint8_t *>(registers) + sizeof(GPRs),
sizeof(_vectorHalfRegisters));
#if __has_feature(ptrauth_calls)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth adding a comment explaining this piece of code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • seems reasonable

}

// arm64_32 and i386 simulator hack
void loadAndAuthenticateLinkRegister(uint32_t srcLinkRegister,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that I get the point of this function overload here. Could you please clarify what do you mean by "arm64_32 and i386 simulator hack"? Deleting this locally did not harm compilation, and I'm not even sure that it's used somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is code we had downstream, I'm not sure of the reason for it, however if we switch to a model where we just gate the use of these functions on targeting arm64e and don't require every arch provide stub funcs I think that becomes moot?

#if __has_feature(ptrauth_calls)
// This is only used for tests where we compare against what happens to be
// signed pointers.
FP = ptrauth_sign_unauthenticated(FP, VALID_CODE_KEY, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VALID_CODE_KEY is not defined, have you forgotten that? In some tests, there is #define VALID_CODE_KEY 0 definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible this is a mis-merge/copy - I'll try to work out where VALID_CODE_KEY is expected to come from and ensure that I pull that def in.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this entire change to compiler-rt/lib/profile/InstrProfilingValue.c may be unrelated to libunwind.

#if __has_feature(ptrauth_calls)
// This is only used for tests where we compare against what happens to be
// signed pointers.
FP = ptrauth_sign_unauthenticated(FP, VALID_CODE_KEY, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to use ptrauth_sign_unauthenticated, you need to include ptrauth.h:

#if __has_include(<ptrauth.h>)
#include <ptrauth.h>
#endif

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm, I'll see if this actually caused any of the bot failures, and if it's not responsible for any I'll try to work out why

@kovdan01 @asl do you folk have a vm setup you can test on? I realize I'm currently limited to building for darwin so it's easy for me to luck out on implicit/transitive includes from other places.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 @asl do you folk have a vm setup you can test on? I realize I'm currently limited to building for darwin so it's easy for me to luck out on implicit/transitive includes from other places.

@ojhunt Yeah, we have that and we can run llvm-test-suite and some other private pauth-specific tests. And, of course, we have a build of pauth-enabled linux toolchain configured.

Basically, having these is the reason why I'm able to add such comments in this PR :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 I mean "do you have step by step instructions for a muppet like me that would make it possible for me to build/test locally" :D

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 I mean "do you have step by step instructions for a muppet like me that would make it possible for me to build/test locally" :D

@ojhunt Ah, thanks for clarifying your question :) Unfortunately, right now our build scripts, instructions, tests, ... are not available publicly. We'll definitely publish them, but right now I guess that we would be happy to test your changes on our side manually. This would, of course, slow down the work on the PR, but at this point it looks like that many of the comments we left actually are relevant for all targets, not only for linux. So, at least for such comments you'll be able to test things using your system w/o our linux-specific stuff.

if (ptrauth_auth_and_resign((void *)pc, ptrauth_key_return_address, sp,
ptrauth_key_return_address,
sp) != (void *)pc) {
_LIBUNWIND_LOG("Bad unwind through arm64e (0x%llX, 0x%llX)->0x%llX\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that this check would be cross-platform and will do the trick for ELF platforms as well, so it's probably worth to re-phrase this and avoid arm64e term but say something in generic pauth terms.

// it impossible to directly cast them without breaking the authentication,
// as a result we need this pair of helpers.
template <typename PtrType>
void set_landing_pad_as_ptr(scan_results& results, const PtrType& out) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PtrType seems to always be a ... pointer type :) So, probably, you can just pass by value instead of using const l-value reference.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 ah yeah, unfortunately that's not sound, but for dumb reasons. The reason I've passed by reference is because the intent is for PtrType to be a ptrauth qualified type on arm64e, and we use a reference to ensure that it remains protected.

It might be possible to make this clearer once we have the schema queries upstreamed, because then we could make it explicit that this expects PtrType to be an address discriminated __ptrauth qualified pointer when compiling on arm64e (or whatever other platforms add ptrauth in future)

@@ -207,7 +211,8 @@ bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
#endif

template <typename A, typename R>
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
typename R::link_reg_t &pc,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please clarify the reason why pc became an out-parameter? I do not see any other changes in the function

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kovdan01 as with the other case it's because link_reg_t is a ptrauth'd type and where intentionally keeping it protected.

@@ -207,7 +211,8 @@ bool DwarfInstructions<A, R>::isReturnAddressSignedWithPC(A &addressSpace,
#endif

template <typename A, typename R>
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pint_t pc,
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this function, we have newRegisters.setIP(returnAddress);. The returnAddress there is unsigned, which causes an error when trying to auth and resign in setIP (at least, this is the case for me locally).

// with the SP
*referenceAuthedLinkRegister = (uint64_t)ptrauth_auth_data(
(void *)inplaceAuthedLinkRegister, ptrauth_key_return_address,
_registers.__sp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like that we need to use &_registers.__pc instead of _registers.__sp because the address was resigned in setIP. Locally, I had an auth failure with this code, and this was resolved by using &_registers.__pc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: alternatively, instead of changing discriminator here, it's possible to change this->getReg(UNW_REG_IP) to _registers.getIP() in callers. This would do resigning with sp, and auth here would pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to work out whether this is me screwing up the lifting of the PR, or me screwing up the authentication changes during cleanup, or something else entirely.

@kovdan01
Copy link
Contributor

@ojhunt JFYI: I've published a couple of small fixes I've applied locally at https://github.com/kovdan01/llvm-project/commits/pointer-authenticated-unwinding-fix0/. Maybe this would be useful

@ojhunt
Copy link
Contributor Author

ojhunt commented Jun 18, 2025

@ojhunt JFYI: I've published a couple of small fixes I've applied locally at https://github.com/kovdan01/llvm-project/commits/pointer-authenticated-unwinding-fix0/. Maybe this would be useful

I'd posted this to try and avoid too much duplicated work so it's highly possible some of these issues are due to me screwing up refactoring, cleanups, or the patch preparation. I'll get back to this after wg21 wraps up this week, so sorry for the delays.

#include <ptrauth.h>
#endif

#if defined(__APPLE__) && __has_feature(ptrauth_qualifier)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am building this for aarch64-linux-pauthtest now and I wonder if it is beneficial to decrease the number of hardcoded defined(__APPLE__) guards? Something like

#if defined(__APPLE__)
#define EXTRA_PTRAUTH_HARDENING 1
#else
#define EXTRA_PTRAUTH_HARDENING 0
#endif

near the top of the file and then use EXTRA_PTRAUTH_HARDENING throughout the code instead of defined(__APPLE__), so that other platforms can opt-in to this hardening by adjusting a single line.

Though, the above EXTRA_PTRAUTH_HARDENING macro still have to be defined multiple times: separate definitions would probably be required for compiler-rt/lib/builtins/gcc_personality_v0.c, libcxxabi and libunwind.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I wonder if there's any existing precedence for cmake variables the apply to all the runtimes?

Also, given some of the other issues you've hit with building don't expend too much effort trying to build/test - I need to work out what parts I've managed to screw up while building out this PR - some of the issues you've hit seem likely to be me screwing up during the preparation of the PR and I don't want you folk to slog through issues caused by my muppetry

#if __has_feature(ptrauth_calls)
// This is only used for tests where we compare against what happens to be
// signed pointers.
FP = ptrauth_sign_unauthenticated(FP, VALID_CODE_KEY, 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this entire change to compiler-rt/lib/profile/InstrProfilingValue.c may be unrelated to libunwind.

Comment on lines +292 to +298
uintptr_t newIP = (uintptr_t)ptrauth_auth_and_resign(
*(void **)&landingPad, ptrauth_key_function_pointer,
existingDiscriminator, ptrauth_key_return_address, stack_pointer);
_Unwind_SetIP(context, newIP);
#else
_Unwind_SetIP(context, landingPad);
#endif
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to assume __has_feature(ptrauth_returns) as well. On the other hand, I doubt we expect any valid build configuration to have __has_feature(ptrauth_calls) == true and __has_feature(ptrauth_returns) == false, so something as simple as

#if !__has_feature(ptrauth_returns)
#error Hardened libunwing expects pac-ret
#endif

should be perfectly enough for the sake of documentating this dependecy and just to be sure :)

But what if we build libunwind on AArch64 with pac-ret, but without extra libunwind hardening (ptrauth_calls feature is false, unsupported platform, etc.): we probably want to manually apply a pac-ret-style protection to the argument passed to _Unwind_SetIP (and we will probably crash at some later point if pac-ret-style protection is not applied). Probably, a simplified version of this code should be added under

      _Unwind_SetIP(context, newIP);
#elif __has_feature(ptrauth_returns)
      // Just sign unprotected newIP with stack pointer and pass it to _Unwind_SetIP
#else
      _Unwind_SetIP(context, landingPad);
#endif

Comment on lines +50 to +54
void(_LIBCXXABI_DTOR_FUNC* _LIBCXXABI_PTRAUTH_FN("__cxa_exception::exceptionDestructor")
exceptionDestructor)(void*);
#endif
std::unexpected_handler unexpectedHandler;
std::terminate_handler terminateHandler;
std::unexpected_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::unexpectedHandler") unexpectedHandler;
std::terminate_handler _LIBCXXABI_PTRAUTH_FN("__cxa_exception::terminateHandler") terminateHandler;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these strings (or, strictly speaking, their hashes) part of public ABI? If so, it may be handy to put them into ptrauth.h, like it is already done for __ptrauth_init_fini_discriminator.

Comment on lines +65 to +68
const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::actionRecord") actionRecord;
const unsigned char* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::languageSpecificData") languageSpecificData;
void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::catchTemp") catchTemp;
void* _LIBCXXABI_PTRAUTH_PDD("__cxa_exception::adjustedPtr") adjustedPtr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

(unw_word_t)ptrauth_auth_and_resign(*(void**)&results.landingPad, _LIBCXXABI_PTRAUTH_KEY, existingDiscriminator,
ptrauth_key_return_address, stack_pointer);
_Unwind_SetIP(context, newIP);
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as in compiler-rt/lib/builtins/gcc_personality_v0.c, do we need an implementation "in between" completely unprotected pointers and full hand-written hardening when only pac-ret is enabled?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
compiler-rt:builtins compiler-rt hardening Issues related to the hardening effort libc++abi libc++abi C++ Runtime Library. Not libc++. libunwind PGO Profile Guided Optimizations
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants